{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6fae82b0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from utils.preprocessing import DataLoader\n",
    "from matplotlib import pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6382b558",
   "metadata": {},
   "source": [
    "# 数据读取"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4b56abca",
   "metadata": {},
   "outputs": [],
   "source": [
    "data_loader_a = DataLoader('data/Data A-Crew.csv', 'data/Data A-Flight.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fe7999a2",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "data_loader_a.dump_data()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7fa040e2",
   "metadata": {},
   "source": [
    "# 模型建立与求解"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffe13b4c",
   "metadata": {},
   "outputs": [],
   "source": [
    "import gurobipy as gp\n",
    "from gurobipy import *"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77a7ea65",
   "metadata": {},
   "source": [
    "## 优化目标①"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c55cc781",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "m=gp.Model('m1')\n",
    "\n",
    "z=m.addVars(data_loader_a.F,vtype=gp.GRB.BINARY,name='z')\n",
    "x_ikdh=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='x_dh')\n",
    "x_ikfo=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='x_fo')\n",
    "x_ikcap=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='x_cap')\n",
    "\n",
    "r_iksta=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='r_iksta')\n",
    "r_jkfin=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='r_jkfin')\n",
    "\n",
    "y_ijk=m.addVars(data_loader_a.F,data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='y_ijk')\n",
    "\n",
    "\n",
    "m.ModelSense=GRB.MINIMIZE\n",
    "\n",
    "m.setObjective(-z.sum())\n",
    "# m.setObjective(x_ikdh.sum())\n",
    "# m.setObjective(gp.quicksum(x_ikfo[i,k] for i in data_loader_a.F for k in data_loader_a.C2))\n",
    "\n",
    "#m.setObjectiveN(-z.sum(),index = 0,weight =0.8)\n",
    "#m.setObjectiveN(x_ikdh.sum(),index=1,weight=0.2)\n",
    "#m.setObjectiveN(gp.quicksum(x_ikfo[i,k] for i in data_loader_a.F for k in data_loader_a.C2),index=2,weight=0.05)\n",
    "\n",
    "\n",
    "M=10000\n",
    "#对X的约束\n",
    "m.addConstrs(x_ikfo[i,k]==0 for i in data_loader_a.F for k in data_loader_a.C1 )\n",
    "m.addConstrs(x_ikcap[i,k]==0 for i in data_loader_a.F for k in data_loader_a.C3 )\n",
    "m.addConstrs(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]<=1 for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "#对Z的约束\n",
    "m.addConstrs(gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for k in data_loader_a.C)<=M*(z[i]) for i in data_loader_a.F)\n",
    "m.addConstrs(x_ikcap.sum(i,'*')== z[i] for i in data_loader_a.F )\n",
    "m.addConstrs(x_ikfo.sum(i,'*')== z[i] for i in data_loader_a.F )\n",
    "m.addConstrs(M*z[j]>=gp.quicksum(x_ikcap[j,k]+x_ikfo[j,k]+x_ikdh[j,k] for k in data_loader_a.C) for j in data_loader_a.F)\n",
    "\n",
    "#对Y的约束\n",
    "m.addConstrs(y_ijk[i,j,k]==0 for i,j in data_loader_a.FF for k in data_loader_a.C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[i,j,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[j,i,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "\n",
    "#对roaster周期的约束\n",
    "m.addConstrs(r_jkfin[j,k]== 0 for j in data_loader_a.nonF_arrive_base[0] for k in data_loader_a.C)\n",
    "m.addConstrs(r_iksta[i,k]== 0 for i in data_loader_a.nonF_leave_base[0] for k in data_loader_a.C)\n",
    "\n",
    "m.addConstrs(gp.quicksum(r_iksta[i,k] for i in data_loader_a.F_leave_base[0] )<=1  for k in data_loader_a.C)\n",
    "m.addConstrs(gp.quicksum(r_jkfin[j,k] for j in data_loader_a.F_arrive_base[0] )-gp.quicksum(r_iksta[i,k] for i in data_loader_a.F_leave_base[0] ) == 0 for k in data_loader_a.C)\n",
    "\n",
    "#第一问中航班对应周期的约束\n",
    "m.addConstrs(1-(y_ijk.sum(i,'*',k)+r_iksta[i,k]+r_jkfin[i,k])<=M*(1-(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])) for i in data_loader_a.F for k in data_loader_a.C)\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k]-(y_ijk.sum('*',i,k)+r_iksta[i,k]) == 0 for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "m.addConstrs(y_ijk.sum('*',i,k)+r_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "#多目标优化\n",
    "# m.addConstr(z.sum()==206)\n",
    "# m.addConstr(x_ikdh.sum()==8)\n",
    "\n",
    "m.update()\n",
    "m.optimize()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d8799037",
   "metadata": {},
   "source": [
    "## 优化目标④"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6ccc7ca7",
   "metadata": {},
   "outputs": [],
   "source": [
    "m=gp.Model('m1')\n",
    "\n",
    "z=m.addVars(data_loader_a.F,vtype=gp.GRB.BINARY,name='z')\n",
    "x_ikdh=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='x_dh')\n",
    "x_ikfo=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='x_fo')\n",
    "x_ikcap=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='x_cap')\n",
    "\n",
    "r_iksta=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='r_iksta')\n",
    "r_jkfin=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='r_jkfin')\n",
    "\n",
    "y_ijk=m.addVars(data_loader_a.F,data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='y_ijk')\n",
    "\n",
    "\n",
    "m.ModelSense=GRB.MINIMIZE\n",
    "\n",
    "# m.setObjective(-z.sum())\n",
    "m.setObjective(x_ikdh.sum())\n",
    "# m.setObjective(gp.quicksum(x_ikfo[i,k] for i in data_loader_a.F for k in data_loader_a.C2))\n",
    "\n",
    "#m.setObjectiveN(-z.sum(),index = 0,weight =0.8)\n",
    "#m.setObjectiveN(x_ikdh.sum(),index=1,weight=0.2)\n",
    "#m.setObjectiveN(gp.quicksum(x_ikfo[i,k] for i in data_loader_a.F for k in data_loader_a.C2),index=2,weight=0.05)\n",
    "\n",
    "\n",
    "M=10000\n",
    "#对X的约束\n",
    "m.addConstrs(x_ikfo[i,k]==0 for i in data_loader_a.F for k in data_loader_a.C1 )\n",
    "m.addConstrs(x_ikcap[i,k]==0 for i in data_loader_a.F for k in data_loader_a.C3 )\n",
    "m.addConstrs(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]<=1 for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "#对Z的约束\n",
    "m.addConstrs(gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for k in data_loader_a.C)<=M*(z[i]) for i in data_loader_a.F)\n",
    "m.addConstrs(x_ikcap.sum(i,'*')== z[i] for i in data_loader_a.F )\n",
    "m.addConstrs(x_ikfo.sum(i,'*')== z[i] for i in data_loader_a.F )\n",
    "m.addConstrs(M*z[j]>=gp.quicksum(x_ikcap[j,k]+x_ikfo[j,k]+x_ikdh[j,k] for k in data_loader_a.C) for j in data_loader_a.F)\n",
    "\n",
    "#对Y的约束\n",
    "m.addConstrs(y_ijk[i,j,k]==0 for i,j in data_loader_a.FF for k in data_loader_a.C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[i,j,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[j,i,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "\n",
    "#对roaster周期的约束\n",
    "m.addConstrs(r_jkfin[j,k]== 0 for j in data_loader_a.nonF_arrive_base[0] for k in data_loader_a.C)\n",
    "m.addConstrs(r_iksta[i,k]== 0 for i in data_loader_a.nonF_leave_base[0] for k in data_loader_a.C)\n",
    "\n",
    "m.addConstrs(gp.quicksum(r_iksta[i,k] for i in data_loader_a.F_leave_base[0] )<=1  for k in data_loader_a.C)\n",
    "m.addConstrs(gp.quicksum(r_jkfin[j,k] for j in data_loader_a.F_arrive_base[0] )-gp.quicksum(r_iksta[i,k] for i in data_loader_a.F_leave_base[0] ) == 0 for k in data_loader_a.C)\n",
    "\n",
    "#第一问中航班对应周期的约束\n",
    "m.addConstrs(1-(y_ijk.sum(i,'*',k)+r_iksta[i,k]+r_jkfin[i,k])<=M*(1-(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])) for i in data_loader_a.F for k in data_loader_a.C)\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k]-(y_ijk.sum('*',i,k)+r_iksta[i,k]) == 0 for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "m.addConstrs(y_ijk.sum('*',i,k)+r_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "#多目标优化\n",
    "m.addConstr(z.sum()==206)\n",
    "# m.addConstr(x_ikdh.sum()==8)\n",
    "\n",
    "m.update()\n",
    "m.optimize()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e630061d",
   "metadata": {},
   "source": [
    "## 优化目标⑦"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "89660a8e",
   "metadata": {},
   "outputs": [],
   "source": [
    "m=gp.Model('m1')\n",
    "\n",
    "z=m.addVars(data_loader_a.F,vtype=gp.GRB.BINARY,name='z')\n",
    "x_ikdh=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='x_dh')\n",
    "x_ikfo=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='x_fo')\n",
    "x_ikcap=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='x_cap')\n",
    "\n",
    "r_iksta=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='r_iksta')\n",
    "r_jkfin=m.addVars(data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='r_jkfin')\n",
    "\n",
    "y_ijk=m.addVars(data_loader_a.F,data_loader_a.F,data_loader_a.C,vtype=gp.GRB.BINARY,name='y_ijk')\n",
    "\n",
    "\n",
    "m.ModelSense=GRB.MINIMIZE\n",
    "\n",
    "# m.setObjective(-z.sum())\n",
    "# m.setObjective(x_ikdh.sum())\n",
    "m.setObjective(gp.quicksum(x_ikfo[i,k] for i in data_loader_a.F for k in data_loader_a.C2))\n",
    "\n",
    "#m.setObjectiveN(-z.sum(),index = 0,weight =0.8)\n",
    "#m.setObjectiveN(x_ikdh.sum(),index=1,weight=0.2)\n",
    "#m.setObjectiveN(gp.quicksum(x_ikfo[i,k] for i in data_loader_a.F for k in data_loader_a.C2),index=2,weight=0.05)\n",
    "\n",
    "\n",
    "M=10000\n",
    "#对X的约束\n",
    "m.addConstrs(x_ikfo[i,k]==0 for i in data_loader_a.F for k in data_loader_a.C1 )\n",
    "m.addConstrs(x_ikcap[i,k]==0 for i in data_loader_a.F for k in data_loader_a.C3 )\n",
    "m.addConstrs(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]<=1 for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "#对Z的约束\n",
    "m.addConstrs(gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for k in data_loader_a.C)<=M*(z[i]) for i in data_loader_a.F)\n",
    "m.addConstrs(x_ikcap.sum(i,'*')== z[i] for i in data_loader_a.F )\n",
    "m.addConstrs(x_ikfo.sum(i,'*')== z[i] for i in data_loader_a.F )\n",
    "m.addConstrs(M*z[j]>=gp.quicksum(x_ikcap[j,k]+x_ikfo[j,k]+x_ikdh[j,k] for k in data_loader_a.C) for j in data_loader_a.F)\n",
    "\n",
    "#对Y的约束\n",
    "m.addConstrs(y_ijk[i,j,k]==0 for i,j in data_loader_a.FF for k in data_loader_a.C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[i,j,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[j,i,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "\n",
    "#对roaster周期的约束\n",
    "m.addConstrs(r_jkfin[j,k]== 0 for j in data_loader_a.nonF_arrive_base[0] for k in data_loader_a.C)\n",
    "m.addConstrs(r_iksta[i,k]== 0 for i in data_loader_a.nonF_leave_base[0] for k in data_loader_a.C)\n",
    "\n",
    "m.addConstrs(gp.quicksum(r_iksta[i,k] for i in data_loader_a.F_leave_base[0] )<=1  for k in data_loader_a.C)\n",
    "m.addConstrs(gp.quicksum(r_jkfin[j,k] for j in data_loader_a.F_arrive_base[0] )-gp.quicksum(r_iksta[i,k] for i in data_loader_a.F_leave_base[0] ) == 0 for k in data_loader_a.C)\n",
    "\n",
    "#第一问中航班对应周期的约束\n",
    "m.addConstrs(1-(y_ijk.sum(i,'*',k)+r_iksta[i,k]+r_jkfin[i,k])<=M*(1-(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])) for i in data_loader_a.F for k in data_loader_a.C)\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k]-(y_ijk.sum('*',i,k)+r_iksta[i,k]) == 0 for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "m.addConstrs(y_ijk.sum('*',i,k)+r_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "#多目标优化\n",
    "m.addConstr(z.sum()==206)\n",
    "m.addConstr(x_ikdh.sum()==8)\n",
    "\n",
    "m.update()\n",
    "m.optimize()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "63343f5c",
   "metadata": {},
   "source": [
    "# 结果展示与导出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6988c903",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "061d4301",
   "metadata": {},
   "outputs": [],
   "source": [
    "results_dic = {'em_no':[], 'fl_no':[], 'cls':[]}\n",
    "for i in data_loader_a.F:\n",
    "    for k in data_loader_a.C:\n",
    "        if x_ikcap[i,k].x > 0.9:\n",
    "            results_dic['em_no'].append(k)\n",
    "            results_dic['fl_no'].append(i)\n",
    "            results_dic['cls'].append(1)\n",
    "        if x_ikfo[i,k].x > 0.9:\n",
    "            results_dic['em_no'].append(k)\n",
    "            results_dic['fl_no'].append(i)\n",
    "            results_dic['cls'].append(2)\n",
    "        if x_ikdh[i,k].x > 0.9:\n",
    "            results_dic['em_no'].append(k)\n",
    "            results_dic['fl_no'].append(i)\n",
    "            results_dic['cls'].append(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "88373a5b",
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.DataFrame(results_dic)\n",
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ae338f1c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from ResultViewer import RViewer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8325deee",
   "metadata": {},
   "outputs": [],
   "source": [
    "rv = RViewer(data_loader_a, data_cls='a')\n",
    "rv.load_results_from_df(df)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f523f733",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "rv.draw_ef_gantt(save=\"results/q1_a.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e1a727d",
   "metadata": {},
   "outputs": [],
   "source": [
    "df_a = rv.get_results_df_a()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14d33d58",
   "metadata": {},
   "outputs": [],
   "source": [
    "df_a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b60c28be",
   "metadata": {},
   "outputs": [],
   "source": [
    "df_a.to_csv('results/q1_a_UnconveredFlights.csv', index=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "61f57352",
   "metadata": {},
   "outputs": [],
   "source": [
    "df_b = rv.get_results_df_b()\n",
    "df_b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a8e4bc94",
   "metadata": {},
   "outputs": [],
   "source": [
    "df_b.to_csv('results/q1_a_CrewRosters.csv', index=0)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:mim] *",
   "language": "python",
   "name": "conda-env-mim-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
